package org.rain.common.classmeta;

import org.rain.common.exception.BaseException;
import org.rain.common.util.Strs;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.StringJoiner;
import java.util.stream.Collectors;

/**
 * created by yangtong on 2025/4/15 13:22:07
 * <br/>
 * 类元数据
 */
public class ClassMetadata<T> {
    private final Class<T> clazz;
    //类注解
    private final Map<Class<? extends Annotation>, Annotation> classAnnotations;
    //类字段
    private final Map<String, FieldMetadata> fields = new LinkedHashMap<>();
    //类方法
    private final Map<String, MethodMetadata> methods = new LinkedHashMap<>();

    public ClassMetadata(Class<T> clazz) {
        this.clazz = clazz;
        this.classAnnotations = Arrays.stream(clazz.getAnnotations())
                .collect(Collectors.toMap(Annotation::annotationType, a -> a));
        collectFields(clazz);
        collectMethods(clazz);
    }

    public FieldMetadata getField(String name) {
        FieldMetadata field = fields.get(name);
        if (field == null) {
            throw new BaseException("从【" + clazz.getName() + "】类中未找到【" + name + "】字段");
        }
        return field;
    }

    public MethodMetadata getMethod(String name, Class<?>... paramTypes) {
        String methodKey = buildMethodKey(name, paramTypes);
        MethodMetadata method = methods.get(methodKey);
        if (method == null) {
            throw new BaseException("从【" + clazz.getName() + "】类中未找到签名为【" + methodKey + "】的方法");
        }
        return method;
    }

    public <A extends Annotation> A getAnnotation(Class<A> type) {
        return (A) classAnnotations.get(type);
    }

    public boolean hasAnnotation(Class<? extends Annotation> type) {
        return classAnnotations.containsKey(type);
    }

    private void collectFields(Class<?> type) {
        while (type != null && type != Object.class) {
            for (Field field : type.getDeclaredFields()) {
                //子类优先
                fields.putIfAbsent(field.getName(), new FieldMetadata(field));
            }
            type = type.getSuperclass();
        }
    }

    private void collectMethods(Class<?> type) {
        while (type != null && type != Object.class) {
            for (Method method : type.getDeclaredMethods()) {
                String key = buildMethodKey(method.getName(), method.getParameterTypes());
                //子类优先
                methods.putIfAbsent(key, new MethodMetadata(method));
            }
            type = type.getSuperclass();
        }
    }

    private String buildMethodKey(String methodName, Class<?>[] parameterTypes) {
        StringJoiner sj = new StringJoiner(", ");
        for (Class<?> parameterType : parameterTypes) {
            sj.add(parameterType.getName());
        }
        return Strs.format("{}({})", methodName, sj.toString());
    }
}
